package ga_yad2;

import genetic_algorithm.Chromosome;
import genetic_algorithm.Crossover;
import genetic_algorithm.FitnessFunction;
import genetic_algorithm.GeneticAlgorithm;
import genetic_algorithm.Mutation;
import genetic_algorithm.Population;
import genetic_algorithm.Selection;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;

import utils.GAretVal;
import utils.RandomGenerator;

public class Yad2Main {

	private static final int MAX_GENS = 200; // maximal number of generations to run
	private static final int CROSSOVER_MODE = Yad2Crossover.PERMUTATION; // the mode of crossover;
	private static final int MUTATION_MODE = Yad2Mutation.EXHAUSTIVE; // the mode of mutation;
	public static  final int POP_SIZE = 200; // number of chromosomes in the population
	private static final double CROSSOVER_RATE = 0.95; // chance to perform crossover
	private static final double MUTATION_RATE = 0.2; // chance to perform mutation	
	private static boolean REMOVE_PARENTS = false; // if chromosomes can be selected only once
	private static final int ELITE_SIZE = 2; // number of elite chromosomes
		
	private static final String FILE_NAME = "Ex2_Input_Example.txt"; // "Ex2_Input_Example.txt"; // path to input file
	private static final int NUM_PRODUCTS = 10; // number of products in system // TODO CHECK IF SHOULD BE PUBLIC
	
	/**
	 * TODO
	 * @param args
	 */
	public static void main(String[] args) {
		
		RandomGenerator.showSeed(); // TODO DEBUG PRINT!!!
		
		// initialize system
		List<int[]> preferences = new LinkedList<int[]>();
		List<Character> products = new LinkedList<Character>();
		List<int[]>  productsSplit = new LinkedList<int[]>();
		parseData(preferences, products, productsSplit);		
		Yad2Fitness.initPreference(preferences);
		Yad2Chromosome.initProducts(productsSplit, products);
		
		// allocate objects implementing each phase in the genetic algorithm
		FitnessFunction fitnessFunction = new Yad2Fitness();
		Crossover crossover = new Yad2Crossover(CROSSOVER_MODE);
		Selection selector = new Yad2Selector(REMOVE_PARENTS);
		Mutation mutation = new Yad2Mutation(fitnessFunction, MUTATION_MODE);
		
		// create initial population
		List<Chromosome> chromList = new LinkedList<Chromosome>();		
		for (int i = 0; i < POP_SIZE; ++i) {
			chromList.add(new Yad2Chromosome());
		}
		Population population = new Population(chromList, fitnessFunction);


		// initialize the genetic algorithm
		GeneticAlgorithm GA = new GeneticAlgorithm(fitnessFunction, crossover,
				selector, mutation, CROSSOVER_RATE, MUTATION_RATE);

		GAretVal result = GA.execute(population, MAX_GENS, true, ELITE_SIZE);
		// TODO DEBUG PRINT!!!
//		System.out.println("result chromosome: "
//				+ result.getOptSolution().getAllValues());
		// TODO DEBUG PRINT!!!
		System.out.println("result chromosome: " + result.getOptSolution());

		// generate a csv file according to execution results
		result.createsCsvFiles("Yad2Results");	
	}

	/**
	 * Parses data out of the input file 
	 * @param preferences list where i'th element is i'th person's
	 * persons for all products
	 * @param products all products in the system 
	 * @param productsSplit list where i'th element holds offset for
	 * i'th person (among all products) and number of owned products
	 */
	private static void parseData(List<int[]> preferences,
			List<Character> products, List<int[]> productsSplit) {

		// open input file
		Scanner scan = null;
		try {
			scan = new Scanner(new File(FILE_NAME));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		// parse data
		int personOffset = 0; // offset in all products list- used to jump to current person's owned products
		while(scan.hasNext())
		{			
			
			// split line to tokens
			String[] tokens = scan.nextLine().split("\t");
			int[] indexes = new int[2];
			indexes[0] = personOffset; // set offset for current person
			indexes[1] = tokens.length - NUM_PRODUCTS; // set number of products owned by current person
			productsSplit.add(indexes);
			int[] personPref = new int[NUM_PRODUCTS];
			
			// parse preferences for each person
			int i;
			for(i = 0 ; i < NUM_PRODUCTS ; ++i)
			{
				// update current person's preference				
				personPref[i] = Integer.parseInt(tokens[i]);				
			}
			
			// set the parsed preferences
			preferences.add(personPref);
			
			// parse products owned by each person
			for( ; i < tokens.length ; ++i)
			{
				// update current person's owned product
				products.add(tokens[i].charAt(0));
				personOffset++;
			}
		}
	}
}
